﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using Djs.Neuronic.Core;

namespace Djs.Neuronic.UI.Interactive
{
    #region IntArea : základní třída interaktivních objektů, prostý Rectangle včetně veškeré podpory pro Paint, Animate, Childs
    /// <summary>
    /// IntArea : základní třída interaktivních objektů, prostý Rectangle včetně veškeré podpory pro Paint, Animate.
    /// Tento prvek NEMÁ ŽÁDNÉ Childs.
    /// </summary>
    public class IntArea
    {
        #region Konstrukce
        public IntArea()
        {
            this._Init();
        }
        private void _Init()
        {
            this.Visible = true;
            this.RelativeBounds = new RelativeBounds(this);
            this.BackColor = Color.LightCyan;
            this.MouseCurrentState = InteractiveMouseState.None;
            this.AnimateMode = AnimateMode.None;
            this.AnimateTime = 2d;
            this.MouseDragMode = Interactive.MouseDragMode.DragParent;
        }
        #endregion
        #region Public property
        /// <summary>
        /// Parent (hostitel) tohoto objektu
        /// </summary>
        public IntContainer Parent { get; internal set; }
        /// <summary>
        /// true, pokud tento objekt má parenta.
        /// false pokud nemá = měl by to být Desktop.
        /// </summary>
        public bool HasParent { get { return (this.Parent != null); } }
        /// <summary>
        /// Reference na nejvyšší Parent = ten, který je přímo umístěn do Desktopu (ten nemá svého parenta).
        /// </summary>
        public virtual IntContainer TopParent { get { return (this.HasParent ? this.Parent.TopParent : (IntContainer)this); } }
        /// <summary>
        /// Kurzor, který se zobrazuje nad tímto prvkem
        /// </summary>
        public SysCursorType Cursor { get; set; }
        /// <summary>
        /// Objekt (a jeho Childs) je viditelný
        /// </summary>
        public bool Visible { get; set; }
        /// <summary>
        /// Barva pozadí
        /// </summary>
        public virtual Color BackColor { get; set; }
        /// <summary>
        /// Generátor barvy pozadí. Pokud není null, použije se přednostně před BackColor.
        /// </summary>
        public virtual Func<Rectangle, Brush> BackBrushGenerator { get; set; }
        /// <summary>
        /// Souřadnice tohoto objektu vzhledem k jeho Parentu.
        /// </summary>
        public virtual Rectangle Bound { get { return this.RelativeBounds.Bound; } set { this.RelativeBounds.Bound = value; } }
        /// <summary>
        /// Ukotvení objektu vzhledem k jeho parentu
        /// </summary>
        public AnchorStyles Anchor { get { return this.RelativeBounds.Anchor; } set { this.RelativeBounds.Anchor = value; } }
        /// <summary>
        /// Objekt řešící relativní umístění this v rámci jeho Parenta (na základě Anchor)
        /// </summary>
        protected RelativeBounds RelativeBounds { get; set; }
        /// <summary>
        /// Souřadnice tohoto objektu vzhledem k desktopu (desktop = hostitelský objekt DblPanel, fyzický WinForm.Control).
        /// </summary>
        public Rectangle DesktopBound
        {
            get
            {
                Rectangle bound = this.Bound;
                Point location = bound.Location;
                IntArea area = this.Parent;
                while (area != null)
                {
                    location = location.Add(area.Bound.Location);
                    area = area.Parent;
                }
                return new Rectangle(location, bound.Size);
            }
        }
        /// <summary>
        /// Souřadnice (v koordinátech Desktopu), kam se naposledy objekt vykreslil.
        /// Hodnotu určuje a plní metoda IntArea.PaintAll().
        /// Souřadnice zahrnují i oblasti, které nejsou aktuálně viditelné = například jsou posunuté v rámci parenta mimo jeho viditelnou část.
        /// Tyto souřadnice se tedy používají pro generování Brush, psaní textu atd. 
        /// Ale nejde o reálně viditelné souřadnice, ty jsou uvedeny ve VisibleBound.
        /// </summary>
        public Rectangle? PaintBound { get; set; }
        /// <summary>
        /// Podmnožina PaintBound, která je nyní reálně vidět.
        /// </summary>
        public Rectangle? VisibleBound { get; set; }
        /// <summary>
        /// Souřadnice (v koordinátech Desktopu), kde je objekt interaktivní
        /// </summary>
        public Rectangle? InteractiveBound { get; set; }
        /// <summary>
        /// Umístění bodu počátku tohoto objektu v souřadnicích relativních k Desktopu (Souřadnice 0/0 je levý horní roh fyzického paneli DblPanel).
        /// </summary>
        public Point DesktopLocation
        {
            get
            {
                IntArea area = this;
                Point location = Point.Empty;
                while (area != null)
                {
                    location = location.Add(area.Bound.Location);
                    area = area.Parent;
                }
                return location;
            }
        }
        #endregion
        #region Animace
        #region Animace - základní metody (příznaky, kroky)
        /// <summary>
        /// Zde objekt nastaví true, pokud chce být animován.
        /// Jakmile se sem nastaví true, pak se zapne timer v základně, 
        /// a 25x za sekundu (po 40 milisekundách, viz konstanta TIMER_STEP_MS) je vyvolána metoda this.Animate a následně překreslení celého objektu.
        /// Vložení false nezastaví timer, ale bude vyhodnoceno spolu se všemi ostatními objekty.
        /// </summary>
        public bool IsAnimated
        {
            get { return this._IsAnimated; }
            set
            {
                this._IsAnimated = value;
                if (value)
                    this.TopParent.AnimateRun();
            }
        }
        /// <summary>
        /// Property vrátí true, pokud existuje alespoň jeden objekt, který má nastaveno IsAnimated = true.
        /// Může to být buď this (nebo kdokoliv z this.Childs, neboť property je virtuální a potomstvo které má Childs ji přepisuje).
        /// Tato property testuje jen k prvnímu objektu, který má IsAnimated = true, a do objektu nic nesděluje.
        /// </summary>
        public virtual bool IsAnyAnimated
        {
            get { return (this.Visible && this.IsAnimated); }
        }
        /// <summary>
        /// Pokud this je viditelné a animované (IsAnimated), zavolá zdejší virtuální metodu Animate().
        /// Metoda je virtuální, takže potomstvo které obsahuje Childs ji může přepsat a řešit i své potomky.
        /// </summary>
        internal virtual void AnimateAll()
        {
            if (this.Visible)
            {
                if (this.IsAnimated)
                    this.AnimateStep();

                this.AnimateAfter();
            }
        }
        /// <summary>
        /// V této metodě má objekt provést svůj jeden krok animace. Neřeší tedy své případné potomstvo.
        /// Metoda je volána pouze pro ty objekty, které jsou animované (jsou Visible a mají IsAnimated = true), je volána 25 x za sekundu.
        /// Objekt v ní pozmění svoje vlastnosti, ale nevykresluje se (v podstatě nemá jak). 
        /// Totiž: animovaných objektů může být více než jeden, a vykreslí se všechny najednou!
        /// Následně je vykreslen celý Desktop (=všechny Childs objekty), čímž se provede animace (včetně pohybu ve vrstvách).
        /// </summary>
        protected virtual void AnimateStep()
        {
            this._AnimateSetValue(true);
        }
        /// <summary>
        /// Metoda je vyvolaná pro každý viditelný objekt v čase animace, ale nezáleží na tom zda objekt sám je IsAnimated.
        /// Cílem je předat požadavek na animaci do Childs.
        /// Pokud potomek obsahuje Childs, pak by je měl animovat v této metodě = jednoduše vyvoláním Childs.Foreach().AnimateAll();
        /// </summary>
        protected virtual void AnimateAfter() { }
        /// <summary>
        /// Proměnná obsahující stav animace
        /// </summary>
        private bool _IsAnimated;
        /// <summary>
        /// Někdo (já, nebo Child od mého potomstva) zahájil animaci (nastavil IsAnimated = true),
        /// property IsAnimated vyvolá událost TopParent.AnimateRun() (TopParent = typicky Desktop).
        /// Konkrétní objekt by měl zareagovat (přes override této metody) = nastartovat Timer, pokud zrovna neběží.
        /// </summary>
        internal virtual void AnimateRun()
        { }
        /// <summary>
        /// Počet milisekund na jeden krok Animate()
        /// </summary>
        protected const int TIMER_STEP_MS = 20;
        #endregion
        #region Animace - fyzická animace klíčové hodnoty AnimatedValue, v různých režimech
        /// <summary>
        /// Režim animace: 
        /// None = bez animace;
        /// Up = přejde z barvy AnimateColor1 do AnimateColor2 během času AnimateTime, a tam zastaví;
        /// Down = přejde z barvy AnimateColor2 do AnimateColor1 během času AnimateTime, a tam zastaví;
        /// Cycle = střídá oba cykly Up a Down, každý v čase AnimateTime, čas bude tedy celkem 2x AnimateTime.
        /// </summary>
        public AnimateMode AnimateMode { get { return this._AnimateMode; } set { this._SetAnimateMode(value); } }
        /// <summary>
        /// Čas v sekundách na cyklus animace od začátku do konce.
        /// V průběhu tohoto času se změní hodnota AnimatedValue od 0 do 1
        /// </summary>
        public double AnimateTime { get { return this._AnimateTime; } set { this._AnimateTime = value; this._SetAnimateTime(); } }
        /// <summary>
        /// Aktuální hodnota animace v rozsahu 0 až 1, podle aktuálního režimu.
        /// Animaci hodnoty zajišťuje bázová třída IntArea.
        /// </summary>
        public double AnimatedValue { get { return this._AnimatedValue; } }
        /// <summary>
        /// Metoda animace, která je vyvolaná v procesu animace po určení nové hodnoty AnimatedValue (závislé na režimu a čase).
        /// Bázová třída animaci vypočítá a tuto metodu zavolá, ale metodu neimplementuje = to řeší konkrétní potomek.
        /// </summary>
        protected virtual void Animate() { }
        /// <summary>
        /// Háček pro potomky, volaný v okamžiku zahájení animace.
        /// V tuto chvíli by se neměly měnit parametry animace (právě změnou parametrů se nastartovala animace, a došlo by k rekurzi).
        /// </summary>
        protected virtual void AnimationStart() { }
        /// <summary>
        /// Událost volaná v okamžiku zahájení animace.
        /// V tuto chvíli by se neměly měnit parametry animace (právě změnou parametrů se nastartovala animace, a došlo by k rekurzi).
        /// </summary>
        public event EventHandler AnimationStarted;
        /// <summary>
        /// Háček pro potomky, volaný v okamžiku ukončení animace.
        /// Pokud potomek změní parametry animace (například ji nastartuje s novými parametry), provede se to korektně.
        /// </summary>
        protected virtual void AnimationStop() { }
        /// <summary>
        /// Událost volaná v okamžiku ukončení animace.
        /// Pokud potomek změní parametry animace (například ji nastartuje s novými parametry), provede se to korektně.
        /// </summary>
        public event EventHandler AnimationStopped;
        private void _SetAnimateMode(AnimateMode mode)
        {
            this._AnimateMode = mode;
            switch (mode)
            {
                case AnimateMode.None:
                    this.IsAnimated = false;
                    break;
                case AnimateMode.UpSinusAD:
                    this._AnimateCosinus = true;
                    this._AnimateValue1 = 0d;
                    this._AnimateValue2 = 180d;
                    this._AnimateCurrentValue = this._AnimateValue1;
                    this._AnimateEndAtValue = this._AnimateValue2;
                    this.IsAnimated = true;
                    break;
                case AnimateMode.DownSinusAD:
                    this._AnimateCosinus = true;
                    this._AnimateValue1 = 180d;
                    this._AnimateValue2 = 360d;
                    this._AnimateCurrentValue = this._AnimateValue1;
                    this._AnimateEndAtValue = this._AnimateValue2;
                    this.IsAnimated = true;
                    break;
                case AnimateMode.CycleSinus:
                    this._AnimateCosinus = true;
                    this._AnimateValue1 = 0d;
                    this._AnimateValue2 = 360d;
                    this._AnimateCurrentValue = this._AnimateValue1;
                    this._AnimateEndAtValue = null;
                    this.IsAnimated = true;
                    break;
            }
            this._SetAnimateTime();
            this._AnimateSetValue(false);
            if (this.IsAnimated)
            {
                this.AnimationStart();
                if (this.AnimationStarted != null)
                    this.AnimationStarted(this, EventArgs.Empty);
            }
        }
        private void _SetAnimateTime()
        {
            double time = this._AnimateTime;
            if (this.IsAnimated)
            {
                double stepCount = 1000d * time / (double)TIMER_STEP_MS;                     // Počet kroků animace, po kterých se naplní daný čas (čas je dán v sekundách, konstanta času na jeden krok v milisekundách)
                this._AnimateStep = (this._AnimateValue2 - this._AnimateValue1) / stepCount; // Velikost jednoho kroku animace = tak, abych v daném čase (=za daný počet kroků) dosáhl hodnoty dané rozdílem cílové - výchozí hodnoty
            }
        }
        private void _AnimateSetValue(bool setNextValue)
        {
            if (this.IsAnimated)
            {
                bool stopAnimate = false;
                if (setNextValue)
                    this._AnimateCurrentValue += this._AnimateStep;
                
                if (this._AnimateEndAtValue.HasValue)
                {
                    if (this._AnimateCurrentValue > this._AnimateEndAtValue.Value)
                    {
                        this._AnimateCurrentValue = this._AnimateEndAtValue.Value;
                        stopAnimate = true;
                    }
                }
                if (this._AnimateCurrentValue > this._AnimateValue2)
                    this._AnimateCurrentValue = (this._AnimateValue1 + (this._AnimateCurrentValue - this._AnimateValue2));

                this._AnimatedValue = this._AnimateGetValue();
                this.Animate();

                if (stopAnimate)
                {
                    this.IsAnimated = false;
                    this.AnimationStop();
                    if (this.AnimationStopped != null)
                        this.AnimationStopped(this, EventArgs.Empty);
                }
            }
        }
        private double _AnimateGetValue()
        {
            if (this._AnimateCosinus)
            {
                double angle = this._AnimateCurrentValue;
                while (angle > _Angle360deg)
                    angle -= _Angle360deg;
                double cosinus = Math.Cos(_AngleDegToRad * angle);
                return (1d - cosinus) / 2d;
            }
            double value = (this._AnimateValue2 - this._AnimateCurrentValue) / (this._AnimateValue2 - this._AnimateValue1);
            return value;
        }
        private static double _Angle360deg = 360d;
        private static double _Angle360rad = Math.PI * 2d;
        private static double _AngleDegToRad = Math.PI / 180d;
        private AnimateMode _AnimateMode;
        private double _AnimateTime;
        private bool _AnimateCosinus;
        private double _AnimateValue1;
        private double _AnimateValue2;
        private double _AnimateCurrentValue;
        private double? _AnimateEndAtValue;
        private double _AnimateStep;
        private double _AnimatedValue;
        #endregion
        #endregion
        #region Paint
        internal virtual void PaintAll(PaintEventArgs e, Rectangle parentPaintBound)
        {
            if (this.Visible)
            {
                Rectangle desktopBound = this.DesktopBound;
                this.PaintBound = desktopBound;
                Rectangle clipBound = Rectangle.Intersect(desktopBound, parentPaintBound);
                this.VisibleBound = clipBound;
                this.InteractiveBound = clipBound;
                e.Graphics.SetClip(clipBound);
                this.Paint(e);
                this.PaintAfter(e, clipBound);
            }
            else
            {
                this.PaintBound = null;
                this.InteractiveBound = null;
            }
        }
        /// <summary>
        /// Metoda, kde objekt vykreslí sám sebe.
        /// Jádro poté vyvolá virtuální metodu PaintAfter() (kde může být provedeno vykreslení všech jeho Childs).
        /// </summary>
        /// <param name="e"></param>
        protected virtual void Paint(PaintEventArgs e)
        {
            if (this.BackBrushGenerator != null)
            {
                using (Brush brush = this.BackBrushGenerator(this.PaintBound.Value))
                {
                    e.Graphics.FillRectangle(brush, this.VisibleBound.Value);
                }
            }
            else
            {
                using (SolidBrush brush = new SolidBrush(this.BackColor))
                {
                    e.Graphics.FillRectangle(brush, this.VisibleBound.Value);
                }
            }
        }
        /// <summary>
        /// Zde je úkolem vykreslit potmstvo (předat každému potomkovi řízení do jeho metody PaintAll())
        /// Bázová třída tuto metodu neimplementuje, ale vyvolá ji z metody PaintAll().
        /// </summary>
        /// <param name="e"></param>
        protected virtual void PaintAfter(PaintEventArgs e, Rectangle clipBound) { }
        /// <summary>
        /// Zajistí překreslení obsahu celého controlu.
        /// Volat s rozmyslem, teprve po provedení všech změn.
        /// </summary>
        public virtual void Refresh()
        {
            this.TopParent.Refresh();
        }
        #endregion
        #region Interaktivita
        /// <summary>
        /// Metoda vrátí objekt, který se nachází na dané souřadnici na nejvyšší Z-Order pozici
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        internal virtual IntArea FindObjectAtPoint(Point point)
        {
            IntArea result = null;
            if (this.Visible && this.InteractiveBound.HasValue)
            {   // Pokud jsem vidět, a mám uložené souřadnice kde jsem byl posledně vykreslen:
                if (this.InteractiveBound.Value.Contains(point) && this.IsMyPoint(point))
                {   // Pokud na dané souřadnici ležím já:
                    result = this;
                }
            }
            return result;
        }
        /// <summary>
        /// Virtuální metoda pro potomky, kteří chtějí část svého prostoru 
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        protected virtual bool IsMyPoint(Point point)
        {
            return true;
        }
        /// <summary>
        /// Aktuální stav myši
        /// </summary>
        protected InteractiveMouseState MouseCurrentState { get; set; }
        /// <summary>
        /// Režim přesouvání objektu pomocí myši
        /// </summary>
        public MouseDragMode MouseDragMode { get; set; }
        #endregion
        #region Interaktivita: virtual háčky a eventy
        internal void InternalMouseStateChanged(InteractiveMouseArgs e)
        {
            this.MouseCurrentState = GetMouseState(e.ChangeType);
            this.OnMouseStateChanged(e);
            if (this.MouseStateChanged != null)
                this.MouseStateChanged(this, e);

            switch (e.ChangeType)
            {
                case InteractiveMouseChangeType.MouseEnter:
                    this.OnMouseEnter(e);
                    if (this.MouseEnter != null)
                        this.MouseEnter(this, e);
                    break;
                case InteractiveMouseChangeType.MouseLeave:
                    this.OnMouseLeave(e);
                    if (this.MouseLeave != null)
                        this.MouseLeave(this, e);
                    break;
                case InteractiveMouseChangeType.MouseMove:
                    this.OnMouseMove(e);
                    if (this.MouseMove != null)
                        this.MouseMove(this, e);
                    break;
                case InteractiveMouseChangeType.MouseDownLeft:
                    this.OnMouseDownLeft(e);
                    if (this.MouseDownLeft != null)
                        this.MouseDownLeft(this, e);
                    break;
                case InteractiveMouseChangeType.MouseClickLeft:
                    this.OnMouseClick(e);
                    if (this.MouseClick != null)
                        this.MouseClick(this, e);
                    break;
                case InteractiveMouseChangeType.MouseUpLeft:
                    this.OnMouseUpLeft(e);
                    if (this.MouseUpLeft != null)
                        this.MouseUpLeft(this, e);
                    break;
                case InteractiveMouseChangeType.MouseDownRight:
                    this.OnMouseDownRight(e);
                    if (this.MouseDownRight != null)
                        this.MouseDownRight(this, e);
                    break;
                case InteractiveMouseChangeType.MouseClickRight:
                    this.OnMouseRightClick(e);
                    if (this.MouseRightClick != null)
                        this.MouseRightClick(this, e);
                    break;
                case InteractiveMouseChangeType.MouseUpRight:
                    this.OnMouseUpRight(e);
                    if (this.MouseUpRight != null)
                        this.MouseUpRight(this, e);
                    break;
                case InteractiveMouseChangeType.MouseDragBegin:
                    this.OnMouseDragBegin(e);
                    if (this.MouseDragBegin != null)
                        this.MouseDragBegin(this, e);
                    break;
                case InteractiveMouseChangeType.MouseDragMove:
                    this.OnMouseDragMove(e);
                    if (this.MouseDragMove != null)
                        this.MouseDragMove(this, e);
                    break;
                case InteractiveMouseChangeType.MouseDragEnd:
                    this.OnMouseDragEnd(e);
                    if (this.MouseDragEnd != null)
                        this.MouseDragEnd(this, e);
                    break;
            }
        }

        public static InteractiveMouseState GetMouseState(InteractiveMouseChangeType changeType)
        {
            switch (changeType)
            {
                case InteractiveMouseChangeType.None: return InteractiveMouseState.None;
                case InteractiveMouseChangeType.MouseEnter: return InteractiveMouseState.MouseOver;
                case InteractiveMouseChangeType.MouseLeave: return InteractiveMouseState.None;
                case InteractiveMouseChangeType.MouseMove: return InteractiveMouseState.MouseOver;
                case InteractiveMouseChangeType.MouseDownLeft: return InteractiveMouseState.MouseDownLeft;
                case InteractiveMouseChangeType.MouseUpLeft: return InteractiveMouseState.MouseOver;
                case InteractiveMouseChangeType.MouseDownRight: return InteractiveMouseState.MouseDownRight;
                case InteractiveMouseChangeType.MouseUpRight: return InteractiveMouseState.MouseOver;
                case InteractiveMouseChangeType.MouseDragBegin: return InteractiveMouseState.MouseDrag;
                case InteractiveMouseChangeType.MouseDragMove: return InteractiveMouseState.MouseDrag;
                case InteractiveMouseChangeType.MouseDragEnd: return InteractiveMouseState.MouseOver;
            }
            return InteractiveMouseState.None;
        }
        internal virtual void OnMouseStateChanged(InteractiveMouseArgs e) { }
        internal virtual void OnMouseEnter(InteractiveMouseArgs e) { }
        internal virtual void OnMouseLeave(InteractiveMouseArgs e) { }
        internal virtual void OnMouseMove(InteractiveMouseArgs e) { }
        internal virtual void OnMouseDownLeft(InteractiveMouseArgs e) { }
        internal virtual void OnMouseDownRight(InteractiveMouseArgs e) { }
        internal virtual void OnMouseUpLeft(InteractiveMouseArgs e) { }
        internal virtual void OnMouseUpRight(InteractiveMouseArgs e) { }
        internal virtual void OnMouseClick(InteractiveMouseArgs e) { }
        internal virtual void OnMouseRightClick(InteractiveMouseArgs e) { }
        internal virtual void OnMouseDragBegin(InteractiveMouseArgs e) { }
        internal virtual void OnMouseDragMove(InteractiveMouseArgs e) { }
        internal virtual void OnMouseDragEnd(InteractiveMouseArgs e) { }
        public event InteractiveMouseHandler MouseStateChanged;
        public event InteractiveMouseHandler MouseEnter;
        public event InteractiveMouseHandler MouseLeave;
        public event InteractiveMouseHandler MouseMove;
        public event InteractiveMouseHandler MouseDownLeft;
        public event InteractiveMouseHandler MouseDownRight;
        public event InteractiveMouseHandler MouseUpLeft;
        public event InteractiveMouseHandler MouseUpRight;
        public event InteractiveMouseHandler MouseClick;
        public event InteractiveMouseHandler MouseRightClick;
        public event InteractiveMouseHandler MouseDragBegin;
        public event InteractiveMouseHandler MouseDragMove;
        public event InteractiveMouseHandler MouseDragEnd;
        #endregion
        #region Protected static služby - generátory tvarů (GraphicsPath)
        /// <summary>
        /// Vrátí tvar odpovídající zkosenému obdélníku (oseknuté rohy).
        /// </summary>
        /// <param name="bound"></param>
        /// <param name="bevel"></param>
        /// <returns></returns>
        protected static GraphicsPath CreateBevelPath(Rectangle bound, int bevel)
        {
            return CreateBevelPath(bound, bevel, bevel);
        }
        /// <summary>
        /// Vrátí tvar odpovídající zkosenému obdélníku (oseknuté rohy).
        /// </summary>
        /// <param name="bound"></param>
        /// <param name="bevel"></param>
        /// <returns></returns>
        protected static GraphicsPath CreateBevelPath(Rectangle bound, int bevelX, int bevelY)
        {
            GraphicsPath path = new GraphicsPath();
            int bx = bevelX;
            int by = bevelY;
            if (bx == 0 || by == 0)
            {   // Pokud některý bevel == 0, pak výsledek bude vždy vypadat jako obdélník:
                // path.AddRectangle(new Rectangle(bound.Location, new Size(bound.Width - 1, bound.Height - 1)));
                path.AddRectangle(bound);
                return path;
            }
            if (2 * bx >= bound.Width || 2 * by >= bound.Height)
            {
                double angle = (double)bx / (double)by;
                // a další korekce!
            }

            int x1 = bound.X;
            int x2 = x1 + bx;
            int x4 = bound.Right - 1;
            int x3 = x4 - bx;
            int y1 = bound.Y;
            int y2 = y1 + by;
            int y4 = bound.Bottom - 1;
            int y3 = y4 - by;
            path.AddPolygon(new Point[] 
            {
                new Point(x2, y1),
                new Point(x1, y2),
                new Point(x1, y3),
                new Point(x2, y4),
                new Point(x3, y4),
                new Point(x4, y3),
                new Point(x4, y2),
                new Point(x3, y1),
                new Point(x2, y1)
            });
            path.CloseFigure();
            return path;
        }
        /// <summary>
        /// Vrátí tvar odpovídající zkosenému obdélníku (oseknuté rohy).
        /// </summary>
        /// <param name="bound"></param>
        /// <param name="bevel"></param>
        /// <returns></returns>
        protected static GraphicsPath CreateRoundPath(Rectangle bound, int bevel)
        {
            return CreateRoundPath(bound, bevel, bevel);
        }
        /// <summary>
        /// Vrátí tvar odpovídající zkosenému obdélníku (oseknuté rohy).
        /// </summary>
        /// <param name="bound"></param>
        /// <param name="bevel"></param>
        /// <returns></returns>
        protected static GraphicsPath CreateRoundPath(Rectangle bound, int bevelX, int bevelY)
        {
            GraphicsPath path = new GraphicsPath();
            int bx = bevelX;
            int by = bevelY;
            if (bx == 0 || by == 0)
            {   // Pokud některý bevel == 0, pak výsledek bude vždy vypadat jako obdélník:
                // path.AddRectangle(new Rectangle(bound.Location, new Size(bound.Width - 1, bound.Height - 1)));
                path.AddRectangle(bound);
                return path;
            }
            if (2 * bx >= bound.Width || 2 * by >= bound.Height)
            {
                double angle = (double)bx / (double)by;
                // a další korekce!
            }

            int x1 = bound.X;
            int x2 = x1 + bx;
            int x4 = bound.Right - 1;
            int x3 = x4 - bx;
            int xa = bx * 2 / 3;
            int y1 = bound.Y;
            int y2 = y1 + by;
            int y4 = bound.Bottom - 1;
            int y3 = y4 - by;
            int ya = by * 2 / 3;
            path.AddLine(new Point(x2, y1), new Point(x3, y1));
            path.AddBeziers(new Point(x3, y1), new Point(x4 - xa, y1), new Point(x4, y1 + ya), new Point(x4, y2));
            path.AddLine(new Point(x4, y2), new Point(x4, y3));
            path.AddBeziers(new Point(x4, y3), new Point(x4, y4 - ya), new Point(x4 - xa, y4), new Point(x3, y4));
            path.AddLine(new Point(x3, y4), new Point(x2, y4));
            path.AddBeziers(new Point(x2, y4), new Point(x1 + xa, y4), new Point(x1, y4 - ya), new Point(x1, y3));
            path.AddLine(new Point(x1, y3), new Point(x1, y2));
            path.AddBeziers(new Point(x1, y2), new Point(x1, y1 + ya), new Point(x1 + xa, y1), new Point(x2, y1));
            path.CloseFigure();
            return path;
        }
        #endregion
        #region Graphics.State Save & Restore
        /// <summary>
        /// Uloží do sebe zálohu stavu grafiky, v Dispose ji vrátí do původního stavu.
        /// Volitelně umožní nastavit do grafiky hladké okraje čar.
        /// </summary>
        /// <param name="e"></param>
        /// <param name="smooth"></param>
        /// <returns></returns>
        protected static IntGraphicsState GraphicsSave(PaintEventArgs e, bool smooth)
        {
            return new IntGraphicsState(e, smooth);
        }
        protected class IntGraphicsState : IDisposable
        {
            public IntGraphicsState(PaintEventArgs e, bool smooth)
            {
                this._Graphics = e.Graphics;
                this._State = e.Graphics.Save();
                if (smooth)
                {
                    e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
                    e.Graphics.InterpolationMode = InterpolationMode.HighQualityBilinear;
                }
            }
            private Graphics _Graphics;
            private GraphicsState _State;
            void IDisposable.Dispose()
            {
                this._Graphics.Restore(this._State);
            }
        }
        #endregion
    }
    #region Enumy, Delegát a Argumenty pro eventy
    /// <summary>
    /// Režim MouseDrag = přetahování objektu myší
    /// </summary>
    [Flags]
    public enum MouseDragMode
    {
        /// <summary>
        /// Tento objekt nepřesouvat, a ani nepředávat přesouvání do parenta.
        /// </summary>
        None = 0,
        /// <summary>
        /// Lze přesouvat pouze vodorovně
        /// </summary>
        Horizontal = 1,
        /// <summary>
        /// Lze přesouvat pouze svisle
        /// </summary>
        Vertical = 2,
        /// <summary>
        /// Lze přesouvat vodorovně i svisle
        /// </summary>
        DragParent = 4,
        /// <summary>
        /// Tento objekt nepřesouvá sám sebe, ale je "průhledný" = přesouvá svého parenta.
        /// </summary>
        All = Horizontal | Vertical
    }
    /// <summary>
    /// Režimy animace
    /// </summary>
    public enum AnimateMode
    {
        /// <summary>
        /// Není animace
        /// </summary>
        None,
        /// <summary>
        /// Od 0 do 1, na 1 skončit, Náběh i Doběh sinusový (křivka má charakter S)
        /// </summary>
        UpSinusAD,
        /// <summary>
        /// Od 1 do 0, na 0 skončit, Náběh i Doběh sinusový (křivka má charakter opačného S)
        /// </summary>
        DownSinusAD,
        /// <summary>
        /// Od 0 do 1 a pak zpátky k 0 a tak stále dokola, sinusový průběh
        /// </summary>
        CycleSinus
    }
    /// <summary>
    /// Režim, co má objekt se sebou udělat když dokončí animaci ToLow
    /// </summary>
    public enum AnimateAutoLowMode
    {
        None,
        Hide,
        Remove
    }
    /// <summary>
    /// Argumenty pro háčky a eventy od myšoidní interaktivity
    /// </summary>
    public class InteractiveMouseArgs : EventArgs
    {
        public InteractiveMouseArgs(InteractiveMouseChangeType changeType)
        {
            this.ChangeType = changeType;
            this.MousePoint = Point.Empty;
            this.MouseButton = MouseButtons.None;
        }
        public InteractiveMouseArgs(InteractiveMouseChangeType changeType, MouseEventArgs e)
        {
            this.ChangeType = changeType;
            this.MousePoint = e.Location;
            this.MouseButton = e.Button;
        }
        public InteractiveMouseArgs(InteractiveMouseChangeType changeType, Point mousePoint, MouseButtons mouseButton)
        {
            this.ChangeType = changeType;
            this.MousePoint = mousePoint;
            this.MouseButton = mouseButton;
        }
        public InteractiveMouseChangeType ChangeType { get; private set; }
        public Point MousePoint { get; private set; }
        public MouseButtons MouseButton { get; private set; }
    }
    /// <summary>
    /// Předpis handleru pro události na interaktivním objektu vyvolané myší
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    public delegate void InteractiveMouseHandler(object sender, InteractiveMouseArgs args);
    #endregion
    #endregion
    #region IntContainer : třída interaktivních objektů, která umožňuje hostovat v sobě další interaktivní objekty
    /// <summary>
    /// IntContainer : třída interaktivních objektů, která umožňuje hostovat v sobě další interaktivní objekty
    /// </summary>
    public class IntContainer : IntArea
    {
        #region Konstrukce
        public IntContainer()
        {
            this._Init();
        }
        private void _Init()
        {
            this._ChildList = new List<IntArea>();
        }
        protected List<IntArea> _ChildList { get; private set; }
        #endregion
        #region Public property
        /// <summary>
        /// Příznak, že this má potomstvo
        /// </summary>
        public bool HasChilds { get { return (this._ChildList.Count > 0); } }
        /// <summary>
        /// Soupis potomstva
        /// </summary>
        public IEnumerable<IntArea> Childs { get { return this._ChildList; } }
        #endregion
        #region AddChilds / Remove
        /// <summary>
        /// Přidá objekt jako podřízený do this.Childs
        /// </summary>
        /// <param name="child"></param>
        public void AddChild(IntArea child)
        {
            this._AddChild(child);
        }
        /// <summary>
        /// Přidá objekty jako podřízené do this.Childs
        /// </summary>
        /// <param name="area"></param>
        public void AddChilds(params IntArea[] childs)
        {
            foreach (IntArea child in childs)
                this._AddChild(child);
        }
        /// <summary>
        /// Přidá potomka do svých Childs
        /// </summary>
        /// <param name="child"></param>
        private void _AddChild(IntArea child)
        {
            if (child != null)
            {
                child.Parent = this;
                this._ChildList.Add(child);
                if (child.IsAnimated)
                    child.IsAnimated = true;        // Tím propíšu IsAnimated do Desktopu a nastartuji Timer
            }
        }
        /// <summary>
        /// Odebere daný objekt ze svých childs
        /// </summary>
        /// <param name="child"></param>
        public void RemoveChild(IntArea child)
        {
            this._ChildList.RemoveAll(i => Object.ReferenceEquals(i, child));
        }
        #endregion
        #region Animace - rekurze do potomstva (neboť předek potomstvo neimplementuje, ale nabízí virtuální property a metody)
        /// <summary>
        /// Property vrátí true, pokud existuje alespoň jeden objekt, který má nastaveno _IsAnimated = true.
        /// Může to být buď this, nebo kdokoliv z this.Childs.
        /// Tato property testuje jen k prvnímu objektu, který má IsAnimated = true, a do objektu nic nesděluje.
        /// </summary>
        public override bool IsAnyAnimated
        {
            get
            {
                if (this.Visible)
                {
                    if (this.IsAnimated) return true;
                    return this._ChildList.Any(i => i.IsAnyAnimated);
                }
                return false;
            }
        }
        /// <summary>
        /// Metoda je vyvolaná pro každý viditelný objekt v čase animace, ale nezáleží na tom zda objekt sám je IsAnimated.
        /// Cílem je předat požadavek na animaci do Childs.
        /// Pokud potomek obsahuje Childs, pak by je měl animovat v této metodě = jednoduše vyvoláním Childs.Foreach().AnimateAll();
        /// </summary>
        protected override void AnimateAfter()
        {
            // dávám: foreach in ... .ToList() proto, že v procesu Animate může dojít k AnimationStop() 
            //  a v něm k odebrání objektu z kolekce this._ChildList, což je nepřípustné.
            // Ale když budu iterovat jinou kolekci (this._ChildList.ToList()), tak se tomu vyhnu.
            foreach (IntArea i in this._ChildList.ToList())
                i.AnimateAll();
        }
        #endregion
        #region Paint - rekurze do potomstva
        /// <summary>
        /// Zde je úkolem vykreslit potmstvo (předat každému potomkovi řízení do jeho metody PaintAll()).
        /// </summary>
        /// <param name="e"></param>
        protected override void PaintAfter(PaintEventArgs e, Rectangle clipBound)
        {
            foreach (IntArea child in this._ChildList)
                child.PaintAll(e, clipBound);
        }
        #endregion
        #region Interaktivita - rekurze do potomstva
        /// <summary>
        /// Metoda vrátí objekt, který se nachází na dané souřadnici na nejvyšší Z-Order pozici
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        internal override IntArea FindObjectAtPoint(Point point)
        {
            IntArea result = null;
            if (this.Visible && this.InteractiveBound.HasValue)
            {   // Pokud jsem vidět, a mám uložené souřadnice kde jsem byl posledně vykreslen:
                if (this.InteractiveBound.Value.Contains(point) && this.IsMyPoint(point))
                {   // Pokud na dané souřadnici ležím já:
                    result = this;

                    // Pokud mám děti, pak tam mohou být ony (jim poskytuji svůj prostor):
                    if (this.HasChilds)
                    {
                        foreach (IntArea c in this._ChildList)
                        {   // Pokud některý potomek leží na daném bodu, pak výsledkem je on (protože leží "na mě"):
                            IntArea child = c.FindObjectAtPoint(point);
                            if (child != null)
                                // Hledám poslední nalezený objekt (nejvýše na ose Z),
                                // takže každý nalezený objekt zaregistrujeme, ale nedávám break => hledám další:
                                result = child;
                        }
                    }
                }
            }
            return result;
        }
        #endregion
    }
    #endregion
    #region IntDesktop : třída reprezentující desktop = základnu umístěnou na prvku InteractivePanel. Zde se fyzicky řeší myší interaktivita.
    /// <summary>
    /// IntDesktop : třída reprezentující desktop = základnu umístěnou na prvku InteractivePanel.
    /// Zde se řeší myší interaktivita.
    /// </summary>
    public class IntDesktop : IntContainer, IDisposable
    {
        #region Konstrukce
        public IntDesktop(DblPanel desktop)
        {
            this._Desktop = desktop;
            this._EventsInit();
            this._TimerInit();
        }
        private DblPanel _Desktop;
        #endregion
        #region Public property
        /// <summary>
        /// Reference na Desktop objekt.
        /// Tuto referenci má každý objekt (ten, který je OnDesktop ji má přímo, ostatní ji mají ze svého TopParenta).
        /// </summary>
        public DblPanel Desktop { get { return (this._Desktop); } }
        /// <summary>
        /// Souřadnice tohoto objektu vzhledem k jeho Parentu, Parentem je ale Desktop, tedy jde o souřadnice this.Desktop.ClientRectangle.
        /// Setování je bez efektu.
        /// </summary>
        public override Rectangle Bound { get { return this.Desktop.ClientRectangle; } set { } }
        /// <summary>
        /// Fyzický typ kurzoru v Desktop objektu
        /// </summary>
        public SysCursorType DesktopCursor { get { return this.Desktop.CursorType; } set { this.Desktop.CursorType = value; } }
        #endregion
        #region Private MouseInteractivity
        #region Základní eventy
        private void _EventsInit()
        {
            DblPanel host = this._Desktop;
            if (host == null) return;
            host.MouseEnter += new EventHandler(_MouseEnter);
            host.MouseLeave += new EventHandler(_MouseLeave);
            host.MouseMove += new MouseEventHandler(_MouseMove);
            host.MouseDown += new MouseEventHandler(_MouseDown);
            host.MouseUp += new MouseEventHandler(_MouseUp);
            this._MouseDesktopState = InteractiveMouseState.None;
        }
        void _MouseEnter(object sender, EventArgs e)
        {
            this._MouseDesktopState = InteractiveMouseState.MouseOver;
            // Poznámka: nalezení konkrétního objektu a odeslání události do něj řeší zdejší handler _MouseLeave(). Po Enter totiž vždy následuje Move.
        }
        void _MouseLeave(object sender, EventArgs e)
        {
            // Pokud je nějaký objekt pod myší, sdělíme mu, že už není:
            if (this._MouseActiveObject != null)
            {
                this._MouseActiveObject.InternalMouseStateChanged(new InteractiveMouseArgs(InteractiveMouseChangeType.MouseLeave));
            }
            this.DesktopCursor = SysCursorType.Default;
            this._MouseActiveObject = null;
            this._MouseDesktopState = InteractiveMouseState.None;
        }
        void _MouseMove(object sender, MouseEventArgs e)
        {
            switch (this._MouseDesktopState)
            {
                case InteractiveMouseState.None:
                    this._MouseDesktopState = InteractiveMouseState.MouseOver;
                    this._MouseMoveNone(e);
                    break;
                case InteractiveMouseState.MouseOver:
                    this._MouseMoveNone(e);
                    break;
                case InteractiveMouseState.MouseDownLeft:
                    this._MouseMoveLeft(e);
                    break;
                case InteractiveMouseState.MouseDownRight:
                    // Pohyb při stisknutém pravém tlačítku se neřeší.
                    break;
                case InteractiveMouseState.MouseDrag:
                    this._MouseDragMove(e);
                    break;

            }
        }
        void _MouseDown(object sender, MouseEventArgs e)
        {
            this._MouseMoveNone(e);    // Zajistí detekci objektu pod myší, případně výměnu objektů a vyvolání háčků Leave a Enter

            IntArea activeObject = this._MouseActiveObject;
            if (activeObject != null)
            {
                if (e.Button == MouseButtons.Left)
                {
                    this._MouseDesktopState = InteractiveMouseState.MouseDownLeft;
                    Point mousePoint = e.Location;
                    this._MouseDownDesktopPoint = mousePoint;
                    this._MouseDragBound = mousePoint.FromCenter(new Size(4, 4));
                    activeObject.InternalMouseStateChanged(new InteractiveMouseArgs(InteractiveMouseChangeType.MouseDownLeft, e));
                }
                else if (e.Button == MouseButtons.Right)
                {
                    this._MouseDesktopState = InteractiveMouseState.MouseDownRight;
                    activeObject.InternalMouseStateChanged(new InteractiveMouseArgs(InteractiveMouseChangeType.MouseDownRight, e));
                }
            }
        }
        void _MouseUp(object sender, MouseEventArgs e)
        {
            IntArea activeObject = this._MouseActiveObject;
            if (activeObject != null)
            {
                switch (this._MouseDesktopState)
                {
                    case InteractiveMouseState.MouseDownLeft:
                        activeObject.InternalMouseStateChanged(new InteractiveMouseArgs(InteractiveMouseChangeType.MouseClickLeft, e));
                        activeObject.InternalMouseStateChanged(new InteractiveMouseArgs(InteractiveMouseChangeType.MouseUpLeft, e));
                        break;
                    case InteractiveMouseState.MouseDownRight:
                        activeObject.InternalMouseStateChanged(new InteractiveMouseArgs(InteractiveMouseChangeType.MouseClickRight, e));
                        activeObject.InternalMouseStateChanged(new InteractiveMouseArgs(InteractiveMouseChangeType.MouseUpRight, e));
                        break;
                    case InteractiveMouseState.MouseDrag:
                        // Na konci Drag se neposílá Click !!!
                        // Ale pokud objekt nepodporuje Drag, a přitom se stisknutá myš pohybovala (_MouseDesktopState je MouseDrag),
                        //    pak se do objektu pošle MouseUp:
                        if (activeObject.MouseDragMode == Interactive.MouseDragMode.None)
                            activeObject.InternalMouseStateChanged(new InteractiveMouseArgs(InteractiveMouseChangeType.MouseUpLeft, e));
                        else
                            activeObject.InternalMouseStateChanged(new InteractiveMouseArgs(InteractiveMouseChangeType.MouseDragEnd, e));
                        break;
                }
                this._MouseDownDesktopPoint = null;
                this._MouseDownOffset = null;
                this._MouseDragBound = null;
            }
            this._MouseDesktopState = InteractiveMouseState.MouseOver;
        }
        #endregion
        #region pohyb myší bez stisknutí tlačítka
        private void _MouseMoveNone(MouseEventArgs e)
        {
            IntArea active = this.FindObjectAtPoint(e.Location);
            if (active == null)
            {   // Pod myší nikdo není (divné, protože skoro vždy je pode mnou Desktop - kromě DblPanel.Borderu)
                if (this._MouseActiveObject != null)
                {   // Předtím pod myší někdo byl - myš ho opustila:
                    this._MouseActiveObject.InternalMouseStateChanged(new InteractiveMouseArgs(InteractiveMouseChangeType.MouseLeave, e));
                    this.DesktopCursor = SysCursorType.Default;
                    this._MouseDesktopState = InteractiveMouseState.None;
                }
            }
            else
            {   // Pod myší někdo je:
                if (this._MouseActiveObject == null || (this._MouseActiveObject != null && !Object.ReferenceEquals(active, this._MouseActiveObject)))
                {   // Dřív tam nikdo nebyl, anebo tam byl někdo jiný:
                    if (this._MouseActiveObject != null)
                        // Dřív tam byl někdo jiný:
                        this._MouseActiveObject.InternalMouseStateChanged(new InteractiveMouseArgs(InteractiveMouseChangeType.MouseLeave, e));
                    // Teď je pod myší objekt "active":
                    this._MouseActiveObject = active;
                    this._MouseActiveObject.InternalMouseStateChanged(new InteractiveMouseArgs(InteractiveMouseChangeType.MouseEnter, e));
                    this.DesktopCursor = active.Cursor;
                }
            }
        }
        #endregion
        #region pohyb myší se stisknutým levým tlačítkem => drag
        private void _MouseMoveLeft(MouseEventArgs e)
        {
            // Sem přicházím za stavu MouseDownLeft, nikoli MouseDrag (to jdu rovnou do _MouseDragMove()).
            // Zde řeším, zda pohyb myši přesáhl hranici _MouseDragBound: pokud ne, pak nic neřeším:
            Point mousePoint = e.Location;
            if (this._MouseActiveObject == null || !this._MouseDragBound.HasValue || this._MouseDragBound.Value.Contains(mousePoint))
                return;
            // Myš se pohnula mimo rámec _MouseDragBound!

            // Při příštím pohybu (pokud se tedy nepřepneme do MouseDrag) už nebudu hlídat _MouseDragBound, z této metdy vyběhnu o řádek výše:
            this._MouseDragBound = null;

            // Nyní by měl začít MouseDrag. Ale koho se bude týkat?
            this._MouseDragObject = this._GetDragObject(this._MouseActiveObject);
            if (this._MouseDragObject == null)
                return;

            // MouseDrag bude, zahájíme jej, a provedem první pohyb:
            this._MouseDragBegin(e);
            this._MouseDragMove(e);
        }
        private void _MouseDragBegin(MouseEventArgs e)
        {
            Point mousePoint = e.Location;
            IntArea dragObject = this._MouseDragObject;
            this._MouseDownOffset = mousePoint.Sub(dragObject.Bound.Location);
            this._MouseDesktopState = InteractiveMouseState.MouseDrag;
            dragObject.InternalMouseStateChanged(new InteractiveMouseArgs(InteractiveMouseChangeType.MouseDragBegin, e));
        }
        private void _MouseDragMove(MouseEventArgs e)
        {
            IntArea dragObject = this._MouseDragObject;
            if (dragObject == null) return;
            if (dragObject.MouseDragMode == Interactive.MouseDragMode.None) return;        // Objekt může být ve stavu MouseDrag, i když on sám Drag nepodporuje.
            if (!this._MouseDownOffset.HasValue) return;
            Point offset = this._MouseDownOffset.Value;
            Rectangle bound = dragObject.Bound;
            Point target = e.Location.Sub(offset);
            if ((dragObject.MouseDragMode & Interactive.MouseDragMode.Vertical) == 0)
                target.Y = bound.Y;
            if ((dragObject.MouseDragMode & Interactive.MouseDragMode.Horizontal) == 0)
                target.X = bound.X;

            if (target != bound.Location)
            {
                dragObject.Bound = new Rectangle(target, bound.Size);
                dragObject.InternalMouseStateChanged(new InteractiveMouseArgs(InteractiveMouseChangeType.MouseDragMove, e));
                this.Refresh();
            }
        }
        /// <summary>
        /// Metoda vrátí objekt, který budeme přetahovat myší (nebo vrátí null, když žádný).
        /// </summary>
        /// <param name="activeObject"></param>
        /// <returns></returns>
        private IntArea _GetDragObject(IntArea activeObject)
        {
            IntArea testObject = activeObject;
            while (true)
            {
                if (testObject == null) return null;
                switch (testObject.MouseDragMode)
                {
                    case Interactive.MouseDragMode.None:
                        // Testovaný objekt zakazuje pohyb:
                        return null;
                    case Interactive.MouseDragMode.Horizontal:
                    case Interactive.MouseDragMode.Vertical:
                    case Interactive.MouseDragMode.All:
                        // Testovaný objekt se sám může pohybovat:
                        return testObject;
                    case Interactive.MouseDragMode.DragParent:
                        // Testovaný objekt se sám nepohybuje, ale předává řízení do parenta:
                        testObject = testObject.Parent;
                        break;
                }
            }
        }
        #endregion
        #region servis a proměnné
        /// <summary>
        /// Stav myši
        /// </summary>
        private InteractiveMouseState _MouseDesktopState;
        /// <summary>
        /// Souřadnice myši v koordinátech desktopu, na němž se stisklo levé tlačítko.
        /// Pokud je null, pak myš není stisknutá.
        /// </summary>
        private Point? _MouseDownDesktopPoint;
        /// <summary>
        /// Souřadnice prostoru, v němž nezačne MouseDrag (= mrtvá oblast). Je uvedena v koordinátech desktopu, protože v nich chodí eventy od myši.
        /// Jde o prostor o velikosti 4x4 px se středem v bodě, kde se myš stiskla.
        /// Jakmile se stisknutá myš bude pohybovat vně tohoto prostoru, začne Drag.
        /// V době, kdy se provádí Drag, je zde null (už nehlídáme "mrtvou oblast")
        /// </summary>
        private Rectangle? _MouseDragBound;
        /// <summary>
        /// Offset při MouseDrag: = (pozice myši) - (_MouseDragObject.Bound.Location), je určeno při začátku Drag, po určení objektu _MouseDragObject.
        /// Pokud je null, pak myš není stisknutá.
        /// </summary>
        private Point? _MouseDownOffset;
        /// <summary>
        /// Aktivní objekt z hlediska Move, MouseDown, MouseUp.
        /// Objekt, kterého se týká Drag, je uveden v _MouseDragObject.
        /// </summary>
        private IntArea _MouseActiveObject;
        /// <summary>
        /// Objekt, kterého se týká MouseDrag.
        /// Může to být _MouseActiveObject pokud je ochoten se pohybovat, nebo některý z jeho parentů.
        /// </summary>
        private IntArea _MouseDragObject;
        #endregion
        #endregion
        #region Animace - Private Timer
        /// <summary>
        /// Iniciace timeru se provádí pouze u objektu který je IsDesktop
        /// </summary>
        private void _TimerInit()
        {
            this._Timer = new System.Threading.Timer(this._TimerTick, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
        }
        private void _TimerTick(object state)
        {
            this._TimerStop();

            if (this.IsAnyAnimated)
            {
                this.AnimateAll();
                this.Refresh();
                this._TimerStart();
            }
        }
        private void _TimerStart()
        {
            this._Timer.Change(TIMER_STEP_MS, System.Threading.Timeout.Infinite);
            this._TimerRunning = true;
        }
        private void _TimerStop()
        {
            this._Timer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
            this._TimerRunning = false;
        }
        private bool _TimerRunning;
        /// <summary>
        /// Stav timeru, zda běží nebo ne.
        /// Nelze změnit.
        /// </summary>
        public bool TimerRunning { get { return this._TimerRunning; } }
        /// <summary>
        /// Některý člen hierarchie zahájil animaci, událost odchytil 
        /// </summary>
        internal override void AnimateRun()
        {
            if (!this._TimerRunning)
                this._TimerStart();
        }
        private System.Threading.Timer _Timer;
        #endregion
        #region Paint
        public override void Refresh()
        {
            if (this._Desktop != null && !this._Desktop.Disposing && !this._Desktop.IsDisposed)
                this._Desktop.Refresh();
        }
        internal void OnPaintToBuffer(InteractivePanel sender, PaintEventArgs e)
        {
            this.PaintAll(e, this.DesktopBound);
            e.Graphics.ResetClip();
        }
        #endregion
        #region IDisposable Members
        void IDisposable.Dispose()
        {
            if (this._Timer != null)
            {
                this._TimerStop();
                this._Timer.Dispose();
                this._Timer = null;
            }
        }
        #endregion
    }
    #endregion
    #region IntAnimatedColor : základní třída pro objekty, které mají animovanou barvu
    /// <summary>
    /// Objekt, který zajišťuje animaci barvy. Základ animace řeší předek IntArea, zde se zajišťuje transformace dvou barev.
    /// </summary>
    public class IntAnimatedColor : IntContainer
    {
        #region Konstrukce a proměnné, proces animace barvy
        public IntAnimatedColor()
        {
            this._AnimateColor1 = SystemColors.ControlLight;
            this._AnimateColor2 = SystemColors.Info;
            this.AnimateTime = 2d;
        }
        private Color _AnimateColor;
        private Color _AnimateColor1;
        private Color _AnimateColor2;
        /// <summary>
        /// Metoda animace, která je vyvolaná v procesu animace po určení nové hodnoty AnimatedValue (závislé na režimu a čase).
        /// </summary>
        protected override void Animate()
        {
            Color color = this._AnimateColor1;
            if (this.IsAnimated)
            {
                color = this._AnimateColor1.Morph(this._AnimateColor2, (float)this.AnimatedValue);
            }
            this._AnimateColor = color;
        }
        #endregion
        #region Public property definující barvy
        /// <summary>
        /// Aktuální barva, která se používá pro kreslení. Průběžně se mění,
        /// Pokud bude setována, platí jen do nejbližšího kroku animace.
        /// </summary>
        public override Color BackColor { get { return this._AnimateColor; } set { this._AnimateColor = value; } }
        /// <summary>
        /// Barva Low = z této barvy se začíná při animaci ve směru Up (a končí ve směru Down).
        /// Setování je možné i za chodu, objekt reaguje přiměřeně, příští krok animace bude odvozen od nově zadané barvy.
        /// </summary>
        public Color ColorLow { get { return this._AnimateColor1; } set { this._AnimateColor1 = value; this.Animate(); } }
        /// <summary>
        /// Barva High = na této barvě se končí při animaci ve směru Up (a začíná ve směru Down).
        /// Setování je možné i za chodu, objekt reaguje přiměřeně, příští krok animace bude odvozen od nově zadané barvy.
        /// </summary>
        public Color ColorHigh { get { return this._AnimateColor2; } set { this._AnimateColor2 = value; this.Animate(); } }
        /// <summary>
        /// Požadavek na automatické skrývání objektu po dosažení barvy Low při dokončení animace AnimateToLow().
        /// Pokud je true, je vhodné aby barva Low měla složku A == 0 (=zcela průhledná).
        /// </summary>
        public AnimateAutoLowMode AutoLowMode { get; set; }
        #endregion
        #region Public metody pro spuštění animace
        /// <summary>
        /// Zahájí animaci směrem k barvě High
        /// </summary>
        public void AnimateToHigh()
        {
            if (this.AutoLowMode == AnimateAutoLowMode.Hide && !this.Visible)
                this.Visible = false;
            this.AnimateMode = Interactive.AnimateMode.UpSinusAD;
        }
        /// <summary>
        /// Zahájí animaci směrem k barvě High
        /// </summary>
        /// <param name="time">Čas na animaci</param>
        public void AnimateToHigh(double time)
        {
            if (this.AutoLowMode == AnimateAutoLowMode.Hide && !this.Visible)
                this.Visible = false;
            this.AnimateTime = time;
            this.AnimateMode = Interactive.AnimateMode.UpSinusAD;
        }
        /// <summary>
        /// Zahájí animaci směrem k barvě High
        /// </summary>
        /// <param name="time">Čas na animaci</param>
        /// <param name="colorHigh">Cílová barva High</param>
        public void AnimateToHigh(double time, Color colorHigh)
        {
            if (this.AutoLowMode == AnimateAutoLowMode.Hide && !this.Visible)
                this.Visible = false;
            this.ColorHigh = colorHigh;
            this.AnimateTime = time;
            this.AnimateMode = Interactive.AnimateMode.UpSinusAD;
        }
        /// <summary>
        /// Zahájí animaci směrem k barvě Low
        /// </summary>
        public void AnimateToLow()
        {
            this.AnimateMode = Interactive.AnimateMode.DownSinusAD;
        }
        /// <summary>
        /// Zahájí animaci směrem k barvě Low
        /// </summary>
        /// <param name="time">Čas na animaci</param>
        public void AnimateToLow(double time)
        {
            this.AnimateTime = time;
            this.AnimateMode = Interactive.AnimateMode.DownSinusAD;
        }
        /// <summary>
        /// Zahájí animaci směrem k barvě Low
        /// </summary>
        /// <param name="time">Čas na animaci</param>
        /// <param name="colorLow">Cílová barva Low</param>
        public void AnimateToLow(double time, Color colorLow)
        {
            this.ColorLow = colorLow;
            this.AnimateTime = time;
            this.AnimateMode = Interactive.AnimateMode.DownSinusAD;
        }
        /// <summary>
        /// Zahájí animaci směrem k barvě Low
        /// </summary>
        /// <param name="autoLowMode">Hodnota autoHideOnLow: true způsobí, že po dokončení animace ve směru Down se do objektu nastaví Visible = false, a objekt přestane být interaktivní.</param>
        public void AnimateToLow(AnimateAutoLowMode autoLowMode)
        {
            this.AutoLowMode = autoLowMode;
            this.AnimateMode = Interactive.AnimateMode.DownSinusAD;
        }
        /// <summary>
        /// Zahájí animaci směrem k barvě Low
        /// </summary>
        /// <param name="time">Čas na animaci</param>
        /// <param name="autoHideOnLow">Hodnota autoHideOnLow: true způsobí, že po dokončení animace ve směru Down se do objektu nastaví Visible = false, a objekt přestane být interaktivní.</param>
        public void AnimateToLow(double time, AnimateAutoLowMode autoLowMode)
        {
            this.AutoLowMode = autoLowMode;
            this.AnimateTime = time;
            this.AnimateMode = Interactive.AnimateMode.DownSinusAD;
        }
        /// <summary>
        /// Zahájí animaci směrem k barvě Low
        /// </summary>
        /// <param name="time">Čas na animaci</param>
        /// <param name="colorLow">Cílová barva Low</param>
        /// <param name="autoHideOnLow">Hodnota autoHideOnLow: true způsobí, že po dokončení animace ve směru Down se do objektu nastaví Visible = false, a objekt přestane být interaktivní.</param>
        public void AnimateToLow(double time, Color colorLow, AnimateAutoLowMode autoLowMode)
        {
            this.AutoLowMode = autoLowMode;
            this.ColorLow = colorLow;
            this.AnimateTime = time;
            this.AnimateMode = Interactive.AnimateMode.DownSinusAD;
        }
        /// <summary>
        /// Háček pro potomky, volaný v okamžiku zahájení animace.
        /// V tuto chvíli by se neměly měnit parametry animace (právě změnou parametrů se nastartovala animace, a došlo by k rekurzi).
        /// </summary>
        protected override void AnimationStart()
        {
            if (this.AutoLowMode == AnimateAutoLowMode.Hide && !this.Visible)
            {
                this.Visible = true;
            }
        }
        /// <summary>
        /// Háček pro potomky, volaný v okamžiku ukončení animace.
        /// Pokud potomek změní parametry animace (například ji nastartuje s novými parametry), provede se to korektně.
        /// </summary>
        protected override void AnimationStop()
        {
            if (this.AnimateMode == Interactive.AnimateMode.DownSinusAD )
            {
                switch (this.AutoLowMode)
                {
                    case AnimateAutoLowMode.None:
                        break;
                    case AnimateAutoLowMode.Hide:
                        this.Visible = false;
                        break;
                    case AnimateAutoLowMode.Remove:
                        this.Parent.RemoveChild(this);
                        break;
                }
            }
        }
        #endregion
    }
    #endregion
    #region IntLabel : objekt který představuje label
    /// <summary>
    /// Objekt který představuje label.
    /// Je potomkem IntAnimatedColor, dovoluje tedy animovat barvu.
    /// </summary>
    public class IntLabel : IntAnimatedColor
    {
        #region Konstrukce a proměnné
        public IntLabel()
        {
            this.Alignment = ContentAlignment.TopLeft;
            this.Font = SystemFonts.DefaultFont;
        }
        private void _StringFormatPrepare()
        {
            StringFormat format = new StringFormat(StringFormatFlags.LineLimit | StringFormatFlags.FitBlackBox);
            switch (this._Alignment)
            {
                case ContentAlignment.TopLeft:
                    format.Alignment = StringAlignment.Near;
                    format.LineAlignment = StringAlignment.Near;
                    break;
                case ContentAlignment.TopCenter:
                    format.Alignment = StringAlignment.Center;
                    format.LineAlignment = StringAlignment.Near;
                    break;
                case ContentAlignment.TopRight:
                    format.Alignment = StringAlignment.Far;
                    format.LineAlignment = StringAlignment.Near;
                    break;
                case ContentAlignment.MiddleLeft:
                    format.Alignment = StringAlignment.Near;
                    format.LineAlignment = StringAlignment.Center;
                    break;
                case ContentAlignment.MiddleCenter:
                    format.Alignment = StringAlignment.Center;
                    format.LineAlignment = StringAlignment.Center;
                    break;
                case ContentAlignment.MiddleRight:
                    format.Alignment = StringAlignment.Far;
                    format.LineAlignment = StringAlignment.Center;
                    break;
                case ContentAlignment.BottomLeft:
                    format.Alignment = StringAlignment.Near;
                    format.LineAlignment = StringAlignment.Far;
                    break;
                case ContentAlignment.BottomCenter:
                    format.Alignment = StringAlignment.Center;
                    format.LineAlignment = StringAlignment.Far;
                    break;
                case ContentAlignment.BottomRight:
                    format.Alignment = StringAlignment.Far;
                    format.LineAlignment = StringAlignment.Far;
                    break;
            }
            this._StringFormat = format;
        }
        private ContentAlignment _Alignment;
        private StringFormat _StringFormat;
        #endregion
        #region Paint
        protected override void Paint(PaintEventArgs e)
        {
            //using (SolidBrush brush = new SolidBrush(Color.FromArgb(64, Color.SandyBrown)))
            //{
            //    e.Graphics.FillRectangle(brush, this.DesktopBound);
            //}
            if (!String.IsNullOrEmpty(this.Text))
            {
                if (this.Font == null) this.Font = SystemFonts.DefaultFont;
                using (SolidBrush brush = new SolidBrush(this.BackColor))
                {
                    e.Graphics.DrawString(this.Text, this.Font, brush, this.DesktopBound, this._StringFormat);        // Poznámka: TextRenderer.DrawText() neumí transparentní barvy !
                }
            }
        }
        #endregion
        #region Public property
        public ContentAlignment Alignment { get { return this._Alignment; } set { this._Alignment = value; this._StringFormatPrepare(); } }
        public string Text { get; set; }
        public Font Font { get; set; }
        #endregion
    }
    #endregion
    #region IntButton : objekt který představuje label
    /// <summary>
    /// IntButton : objekt který představuje label
    /// </summary>
    public class IntButton : IntAnimatedColor
    {
        #region Konstrukce a proměnné
        public IntButton()
        {
            this._Alignment = ContentAlignment.MiddleCenter;
            this._Enabled = true;
        }
        private ContentAlignment _Alignment;
        private bool _Enabled;
        private Image _ImageDisabled;
        private Image _ImageNormal;
        private Image _ImageHot;
        private Image _ImageDown;
        /// <summary>
        /// Vrátí Image přiměřený aktuálnímu stavu objektu
        /// </summary>
        /// <returns></returns>
        private Image _GetApropriateImage()
        {
            if (!this._Enabled)
                return _GetFirstImage(this._ImageDisabled, this._ImageNormal);

            switch (this.MouseCurrentState)
            {
                case InteractiveMouseState.None:
                    return this._ImageNormal;
                case InteractiveMouseState.MouseOver:
                case InteractiveMouseState.MouseDownRight:
                    return _GetFirstImage(this._ImageHot, this._ImageNormal);
                case InteractiveMouseState.MouseDownLeft:
                case InteractiveMouseState.MouseDrag:
                    return _GetFirstImage(this._ImageDown, this._ImageHot, this._ImageNormal);
            }
            return this._ImageNormal;
        }
        /// <summary>
        /// Vrátí první Image, který není null
        /// </summary>
        /// <param name="images"></param>
        /// <returns></returns>
        private Image _GetFirstImage(params Image[] images)
        {
            foreach (Image image in images)
            {
                if (image != null)
                    return image;
            }
            return null;
        }
        #endregion
        #region Paint
        protected override void Paint(PaintEventArgs e)
        {
            Image image = this._GetApropriateImage();
            if (image != null)
                this._PaintImage(e, image);
        }
        private void _PaintImage(PaintEventArgs e, Image image)
        {
            Rectangle imageBounds = image.Size.AlignTo(this.DesktopBound, this.Alignment);
            e.Graphics.DrawImage(image, imageBounds);
        }
        internal override void OnMouseStateChanged(InteractiveMouseArgs e)
        {
            this.Refresh();
        }
        #endregion
        #region Public property
        public Image ImageNormal { get { return this._ImageNormal; } set { this._ImageNormal = value; } }
        public Image ImageDisabled { get { return this._ImageDisabled; } set { this._ImageDisabled = value; } }
        public Image ImageHot { get { return this._ImageHot; } set { this._ImageHot = value; } }
        public Image ImageDown { get { return this._ImageDown; } set { this._ImageDown = value; } }
        public ContentAlignment Alignment { get { return this._Alignment; } set { this._Alignment = value; } }
        #endregion
    }
    #endregion
    #region IntImage : objekt pro zobrazení obrázku
    /// <summary>
    /// IntImage : objekt pro zobrazení obrázku
    /// </summary>
    public class IntImage : IntArea
    {
        public IntImage()
        {
            this.Alignment = ContentAlignment.MiddleCenter;
            this.BackColor = Color.Empty;
        }
        protected override void Paint(PaintEventArgs e)
        {
            Image image = this.Image;
            if (image != null)
            {
                Rectangle imageBound = image.Size.AlignTo(this.PaintBound.Value, this.Alignment);
                e.Graphics.DrawImage(image, imageBound);
            }
        }
        /// <summary>
        /// Obrázek
        /// </summary>
        public Image Image { get; set; }
        /// <summary>
        /// Umístění obrázku v objektu
        /// </summary>
        public ContentAlignment Alignment { get; set; }
    }
    #endregion
    #region IntProgress : objekt zobrazující progressbar
    /// <summary>
    /// IntProgress : objekt zobrazující progressbar
    /// </summary>
    public class IntProgress : IntContainer
    {
        public IntProgress()
        {
            this.Orientation = IntProgressOrientation.LeftToRight;
            this.AlphaBorder = 255;
            this.AlphaValue = 255;
            this.BorderColor = Color.Black;
            this.BackColor = Color.LightYellow;
            this.ValueColor = Color.Green;
        }
        /// <summary>
        /// Souřadnice tohoto objektu vzhledem k jeho Parentu.
        /// Výška horizontálního objektu (nebo šířka vertikálního) je nejméně 12px.
        /// </summary>
        public override Rectangle Bound
        {
            get
            {
                return base.Bound;
            }
            set
            {
                Rectangle bound = value;
                if (this.IsHorizontal && bound.Height < 12)
                    bound.Height = 12;
                else if (this.IsVertical && bound.Width < 12)
                    bound.Width = 12;
                base.Bound = bound;
            }
        }
        /// <summary>
        /// Orientace progresu. Defaultní je LeftToRight
        /// </summary>
        public IntProgressOrientation Orientation { get; set; }
        /// <summary>
        /// True = horizontální orientace
        /// </summary>
        protected bool IsHorizontal { get { return (this.Orientation == IntProgressOrientation.LeftToRight || this.Orientation == IntProgressOrientation.RightToLeft); } }
        /// <summary>
        /// True = vertikální orientace
        /// </summary>
        protected bool IsVertical { get { return (this.Orientation == IntProgressOrientation.BottomToTop || this.Orientation == IntProgressOrientation.TopToBottom); } }
        /// <summary>
        /// Hodnota progresu. Má mít rozsah 0 až 1.
        /// Hodnoty mimo rozsah nezpůsobí chybu, bude zobrazeny nejbližší odpovídající hodnota.
        /// </summary>
        public double Value { get; set; }
        /// <summary>
        /// Barva rámečku, default = černá
        /// </summary>
        public Color BorderColor { get; set; }
        /// <summary>
        /// Barva hodnoty, default = zelená
        /// </summary>
        public Color ValueColor { get; set; }
        /// <summary>
        /// Hodnota Alpha pro kreslení rámečku a pozadí. Podpora pro animaci průhlednosti. Výchozí hodnota = 255.
        /// </summary>
        protected int AlphaBorder { get; set; }
        /// <summary>
        /// Hodnota Alpha pro kreslení hodnoty Value. Podpora pro animaci průhlednosti. Výchozí hodnota = 255.
        /// </summary>
        protected int AlphaValue { get; set; }
        /// <summary>
        /// Vykreslení
        /// </summary>
        /// <param name="e"></param>
        protected override void Paint(PaintEventArgs e)
        {
            using (GraphicsSave(e, true))
            {
                Rectangle bound = this.PaintBound.Value;
                int bevel = (bound.Height > 16 ? 3 : 2);
                float axis = (this.IsHorizontal ? 0f : 90f);

                // Pozadí, 3D rámeček:
                Rectangle boundOuter = new Rectangle(bound.X, bound.Y, bound.Width - 3, bound.Height - 3);           // Celý tvar = černý rámeček okolo pozadí
                Rectangle boundInner = new Rectangle(bound.X + 1, bound.Y + 1, bound.Width - 5, bound.Height - 5);   // Pozadí (uvnitř Outer)
                Rectangle boundLight = new Rectangle(bound.X + 1, bound.Y + 1, bound.Width - 3, bound.Height - 3);   // Světlý rámeček (3D efekt)
                using (GraphicsPath pathOuter = CreateRoundPath(boundOuter, bevel))
                using (GraphicsPath pathInner = CreateRoundPath(boundInner, bevel))
                using (GraphicsPath pathLight = CreateRoundPath(boundLight, bevel))
                using (LinearGradientBrush lgb0 = new LinearGradientBrush(boundOuter, this._GetAlphaColor(this.BackColor.Morph(Color.Black, 0.10f), false), this._GetAlphaColor(this.BackColor.Morph(Color.White, 0.70f), false), axis + 90f))
                using (Pen borderPen = new Pen(this._GetAlphaColor(this.BorderColor, false)))
                using (Pen lightPen = new Pen(this._GetAlphaColor(Color.LightGray, false)))
                {
                    lgb0.SetBlendTriangularShape(0.15f);
                    e.Graphics.FillPath(lgb0, pathInner);
                    e.Graphics.DrawPath(lightPen, pathLight);
                    e.Graphics.DrawPath(borderPen, pathOuter);
                }

                // Vnitřní plocha progresu (Value):
                Rectangle inner = new Rectangle(bound.X + 1, bound.Y + 2, bound.Width - 5, bound.Height - 6);
                int width = (int)(Math.Round((double)inner.Width * this.Value, 0));
                if (width > 0)
                {
                    using (GraphicsPath path = CreateRoundPath(inner, bevel))
                    using (LinearGradientBrush lgb1 = new LinearGradientBrush(inner, this._GetAlphaColor(this.ValueColor.Morph(Color.Black, 0.25f), true), this._GetAlphaColor(this.ValueColor.Morph(Color.White, 0.65f), true), axis - 90f))
                    {
                        RectangleF? clipBound = null;
                        if (width < inner.Width)
                        {
                            clipBound = e.Graphics.ClipBounds;
                            Rectangle clip = new Rectangle(inner.X, inner.Y, width, inner.Height);
                            e.Graphics.IntersectClip(clip);
                        }
                        e.Graphics.FillPath(lgb1, path);
                        if (clipBound.HasValue)
                            e.Graphics.SetClip(clipBound.Value);
                    }
                }
            }
        }
        protected Color _GetAlphaColor(Color inputColor, bool alphaForValue)
        {
            int alpha = (alphaForValue ? this.AlphaValue : this.AlphaBorder);
            return Color.FromArgb(alpha, inputColor.R, inputColor.G, inputColor.B);
        }
    }
    /// <summary>
    /// Směr přírůstku ukazatele (jeho orientace)
    /// </summary>
    public enum IntProgressOrientation
    {
        /// <summary>
        /// Neurčeno
        /// </summary>
        None,
        /// <summary>
        /// Zleva doprava (defaultní)
        /// </summary>
        LeftToRight,
        /// <summary>
        /// Zprava doleva
        /// </summary>
        RightToLeft,
        /// <summary>
        /// Zespodu nahoru
        /// </summary>
        BottomToTop,
        /// <summary>
        /// Zeshora dolů
        /// </summary>
        TopToBottom
    }
    #endregion
    #region IntTimer : objekt zobrazující čas zbývající do určité události (Automaticky animovaný ProgressBar)
    /// <summary>
    /// IntTimer : objekt zobrazující čas zbývající do určité události (Automaticky animovaný ProgressBar)
    /// </summary>
    public class IntTimer : IntProgress
    {
        /// <summary>
        /// Událost kdy doběhne timer do svého konce
        /// </summary>
        public event EventHandler TimeElapsed;
        /// <summary>
        /// Metoda animace, která je vyvolaná v procesu animace po určení nové hodnoty AnimatedValue (závislé na režimu a čase).
        /// Bázová třída animaci vypočítá a tuto metodu zavolá, ale metodu neimplementuje = to řeší konkrétní potomek.
        /// </summary>
        protected override void Animate()
        {
            this.Value = this.AnimatedValue;
            this.AlphaValue = (int)(255d * this.Value);
            this.AlphaBorder = this.AlphaValue;
        }
        protected override void AnimationStop()
        {
            base.AnimationStop();
            this.OnTimeElapsed();
        }
        protected virtual void OnTimeElapsed()
        {
            if (this.TimeElapsed != null)
                this.TimeElapsed(this, EventArgs.Empty);
        }
    }
    #endregion
}
